Skip to content

feat(#416): support Android 16 (API 36) - #437

Merged
binokaryg merged 8 commits into
masterfrom
416-support-android-16
Aug 7, 2026
Merged

feat(#416): support Android 16 (API 36)#437
binokaryg merged 8 commits into
masterfrom
416-support-android-16

Conversation

@binokaryg

@binokaryg binokaryg commented Aug 6, 2026

Copy link
Copy Markdown
Member

Closes #416.

Description

Targets Android 16 (API 36) ahead of Google Play's Aug 31, 2026 target API deadline.

Scope is deliberately limited to what the SDK bump requires — every change below has a reproducible failure behind it. Related-but-separable work has been moved out (see Follow-ups below).

Target SDK

  • compileSdk and targetSdkVersion 35 → 36.

Back navigation (required by the bump)

Targeting SDK 36 enables predictive back by default, which bypasses our onBackPressed() overrides. Adding android:enableOnBackInvokedCallback="false" restores current behaviour.

This is a regression fix, not an improvement. A/B tested on an Android 16 emulator by opening the Custom URL form in settings and pressing back:

targetSdk enableOnBackInvokedCallback="false" Back from Custom URL form
35 absent ✅ returns to server list
36 absent ❌ app exits — onBackPressed() never runs
36 present ✅ returns to server list

It works today at 35 and breaks at 36. The failure is silent — the app compiles and runs normally — so it wouldn't show up in a smoke test. The same bypass affects EmbeddedBrowserActivity (the webapp's back handling) and ClosableAppActivity (finishAffinity).

Migrating properly to OnBackPressedDispatcher is worth doing, but it means moving EmbeddedBrowserActivity and ClosableAppActivity off plain Activity and converting OpenSettingsDialogFragment to an AndroidX fragment — too much for this PR.

Tests (required by the bump)

A dependency chain, each link forced by the previous:

  • Robolectric 4.15.1 → 4.16.1. 4.15.1 rejects the new target outright: Package targetSdkVersion=36 > maxSdkVersion=35.
  • mockito-inline 5.2.0 → mockito-core 5.23.0. The old ByteBuddy can't instrument alongside Robolectric 4.16 (Mockito cannot mock this class… Could not modify all classes, 130 failures). mockito-inline is discontinued — inline mocks are the default since Mockito 5.
  • CI Java 17 → 21. Robolectric 4.16 needs a Java 21 runtime to emulate SDK 36; on 17 it fails with UnsupportedOperationException at DefaultSdkProvider.java:170. Compile source/target compatibility stays at 17. Running unit tests locally now requires Java 21.
  • Robolectric 4.16 dropped emulation of SDK < 23, so the two LSmsSender tests force Build.VERSION.SDK_INT via ReflectionHelpers instead of @Config(sdk = 22). The pre-M path is still shipped (minSdkVersion 21) and still tested.

Testing

  • Unit tests: 163 run, green.
  • Android lint (warnings-as-errors), checkstyle, PMD, SpotBugs: pass.
  • assembleUnbrandedDebug and bundleUnbrandedRelease both succeed.
  • Manual verification on a Pixel 9 AVD (API 36) and a physical Galaxy S25+ running Android 16, against a CHT 4.x instance: install, launch, login, initial replication, Tasks/Reports/People navigation, back navigation, and backgrounding/resume all behave as before.

Follow-ups

Moved out of this PR after review:

  • AGP 8.10.1 + Gradle 8.11.1, with a workaround for the ABI-splits-vs-bundle bug that caused the bug: app bundling fails with current gradle version #422 rollback. Verified that AGP 8.8.2 builds fine with compileSdk 36 (it only warns "tested up to compileSdk 35"), so this isn't a prerequisite here — it belongs with bug: app bundling fails with current gradle version #422.
  • Removing the android:screenOrientation="portrait" locks, which the Play Store flags as a recommendation. Not required: the restrictions are only ignored on displays with sw >= 600dp, so phones keep honouring them on Android 16 (and on Android 17 — the opt-out that disappears at API 37 is PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, which we don't use). Whether phones should go adaptive is a product decision.
  • Large-screen rotation state. Tablets rotate these activities regardless of the attribute, and SettingsDialogActivity has no onSaveInstanceState and no configChanges — so rotating while on the custom-URL form drops the user back to the server list. Pre-existing, but large-screen users will hit it.

AI Disclosure

This PR was written primarily by Claude Code (Claude Fable 5), working interactively under my direction: it made the Gradle/manifest/test changes and ran the verification (unit tests, lint/static analysis, APK and bundle builds, the predictive-back A/B test, and manual testing on an Android 16 emulator and device). I reviewed the changes, chose the approach at each decision point, and did the final review of this description.

🤖 Generated with Claude Code

Binod Adhikary and others added 5 commits August 5, 2026 15:35
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Robolectric 4.16 dropped emulation of SDK < 23, so the LSmsSender
tests force Build.VERSION.SDK_INT instead of @config(sdk = 22).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Android 16 ignores orientation locks on large screens and the Play
Store flags them. The predictive back opt-out keeps onBackPressed()
working when targeting SDK 36.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@binokaryg
binokaryg marked this pull request as ready for review August 6, 2026 04:34
@andrablaj andrablaj changed the title # feat(#416): support Android 16 (API 36) feat(#416): support Android 16 (API 36) Aug 6, 2026

@jonathanbataire jonathanbataire left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for starting this
let me have a look

</intent-filter>
</activity>
<activity android:name="EmbeddedBrowserActivity"
android:screenOrientation="portrait"

@jonathanbataire jonathanbataire Aug 7, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this mean it allows landscape for small devices
is this a hard requirement for v16
if android 16 ignores this on large devices then i think we can leave it so small device behavior is not altered
i think it should remain potrait for small devices the webapp may not be well optimized in landscape for small screens

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it allows landscape even on small devices.

It's not a hard requirement though, just something the Play Store flagged as a recommendation (#416 (comment)).

Tablets will still rotate regardless of the attribute.

I did some tesitng and looks like rotating doesn't recreate the activity or reload the webview.

My thinking was to follow the recommendation and keep behaviour consistent across devices, but I'm open to discussion (as noted in the description above). I am happy to restore the attribute if we'd rather not change phone behaviour in this PR.

@jonathanbataire jonathanbataire left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@binokaryg this PR has a lot of great changes but i feel like most having nothing to do with targeting sdk36
isn't changing compileSdk and targetSdkVersion to 36 enough? am i missing sth
we could move these other improvements into new PRs/issues
FWIW ive tested it with just those changes and it seems to compile and run fine

@binokaryg

binokaryg commented Aug 7, 2026

Copy link
Copy Markdown
Member Author

@binokaryg this PR has a lot of great changes but i feel like most having nothing to do with targeting sdk36 isn't changing compileSdk and targetSdkVersion to 36 enough? am i missing sth we could move these other improvements into new PRs/issues FWIW ive tested it with just those changes and it seems to compile and run fine

During this work, the test suite failed when changing the SDK version.

  • Robolectric 4.15.1 rejects targetSdk 36 outright: Package targetSdkVersion=36 > maxSdkVersion=35
  • Robolectric 4.16.1 then breaks mockito-inline 5.2.0: Mockito cannot mock this class… Could not modify all classes → 130 failures.
  • Robolectric 4.16 needs a Java 21 runtime: CI run 31055233159 failed with UnsupportedOperationException at DefaultSdkProvider.java:170 on Java 17, and only went green on 31066649284 after the Java bump.

AGP 8.10.1 + split ABI was Claude's suggestion sourced from Google's warning: AGP 8.8 warns "tested up to compileSdk 35"
If it compiles fine, we can probably skip it. But it was presented as a fix for #422.

Update:
I've now tested it: 8.8.2 does build with compileSdk 36, it just warns "tested up to compileSdk 35". So it's separable.

@binokaryg

Copy link
Copy Markdown
Member Author

There's one more that is caused by the SDK bump: enableOnBackInvokedCallback="false".
Targeting 36 turns predictive back on by default, which bypasses our onBackPressed() overrides. I A/B tested this on an Android 16 emulator, opening the Custom URL form in settings and pressing back.

targetSdk opt-out flag back from Custom URL form
35 absent ✅ returns to server list
36 absent ❌ app exits (onBackPressed() never runs)
36 present ✅ returns to server list

So it works with 35 but breaks at 36. The flag android:enableOnBackInvokedCallback="false" fixes it.

Binod Adhikary and others added 2 commits August 7, 2026 00:29
AGP 8.8.2 builds fine with compileSdk 36 (warning only), so the
upgrade and its ABI-splits workaround belong with #422 instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keeps small-device behaviour unchanged, per review. Android 16 ignores
these on large screens anyway. The predictive back opt-out stays: it
fixes a regression caused by targeting SDK 36.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@binokaryg

Copy link
Copy Markdown
Member Author

I have removed the extras and kept only necessary bits. I've also updated the PR description above.

@jonathanbataire jonathanbataire left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perfect

@jonathanbataire

jonathanbataire commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

i dont if we should indicate that builds uses java 21 now in the read me
cause i know things gonna break locally for people still using older versions
I dont see any documentation on project java version
idk

@jonathanbataire

Copy link
Copy Markdown
Contributor

i dont if we should indicate that builds uses java 21 now in the read me cause i know things gonna break locally for people still using older versions I dont see any documentation on project java version idk

maybe its sth you can bebug with error logs nvm

Comment thread build.gradle Outdated
Fails fast with a clear error when JDK 21 is missing, instead of Robolectric failing later with UnsupportedOperationException. Covers
test execution as well as compilation.

Co-authored-by: Jonathan <jonathanbataire@gmail.com>
@binokaryg
binokaryg merged commit 757f4d9 into master Aug 7, 2026
8 checks passed
@binokaryg
binokaryg deleted the 416-support-android-16 branch August 7, 2026 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Due Aug 31 2026: Support Android 16

2 participants